package gorm

import (
	"encoding/json"
	"net/http"
	"sync"
	"time"

	"gitee.com/zawei/yggdrasil/pkg/lib"
	"gitee.com/zawei/yggdrasil/pkg/metrics"
	"gitee.com/zawei/yggdrasil/pkg/transport/server/governor"
	"gorm.io/gorm"
)

var instances = sync.Map{}

// Range 遍历所有实例
func Range(fn func(name string, db *gorm.DB) bool) {
	instances.Range(func(key, val interface{}) bool {
		return fn(key.(string), val.(*gorm.DB))
	})
}

// Stats
func Stats() (stats map[string]interface{}) {
	stats = make(map[string]interface{})
	instances.Range(func(key, val interface{}) bool {
		name := key.(string)
		db := val.(*gorm.DB)
		sqlDb, err := db.DB()
		if err != nil {
			return false
		}
		stats[name] = sqlDb.Stats()
		return true
	})
	return
}

func init() {
	type gormStatus struct {
		Gorms map[string]interface{} `json:"gorms"`
	}
	var rets = gormStatus{
		Gorms: make(map[string]interface{}, 0),
	}
	governor.HandleFunc("/debug/gorm/stats", func(w http.ResponseWriter, r *http.Request) {
		rets.Gorms = Stats()
		w.WriteHeader(200)
		encoder := json.NewEncoder(w)
		if r.URL.Query().Get("pretty") == "true" {
			encoder.SetIndent("", "    ")
		}
		_ = encoder.Encode(rets)
	})
	go monitor()
}

func monitor() {
	for {
		time.Sleep(time.Second * 10)
		Range(func(name string, db *gorm.DB) bool {
			sqlDB, err := db.DB()
			if err != nil {
				return false
			}
			stats := sqlDB.Stats()
			_ = metrics.SummaryObserve(lib.MetricsLibHandleStats, float64(stats.Idle), map[string]string{"name": name, "status": "idle"})
			_ = metrics.SummaryObserve(lib.MetricsLibHandleStats, float64(stats.WaitCount), map[string]string{"name": name, "status": "wait"})
			_ = metrics.SummaryObserve(lib.MetricsLibHandleStats, float64(stats.OpenConnections), map[string]string{"name": name, "status": "conns"})
			_ = metrics.SummaryObserve(lib.MetricsLibHandleStats, float64(stats.MaxOpenConnections), map[string]string{"name": name, "status": "max_open_conns"})
			_ = metrics.SummaryObserve(lib.MetricsLibHandleStats, float64(stats.MaxIdleClosed), map[string]string{"name": name, "status": "max_idle_closed"})
			_ = metrics.SummaryObserve(lib.MetricsLibHandleStats, float64(stats.MaxLifetimeClosed), map[string]string{"name": name, "status": "max_lifetime_closed"})
			return true
		})
	}
}
